Appearance
Vue 实战-vue-property-decorator
Vue 实战:vue-property-decorator
我们打开项目,发现最大的不同是额外引入了一个库 vue-property-decorator
。
由于它的存在,我们可以使用基于类的注解装饰器进行开发,这个语法就有点像 Angular 了。
你可能听说过另外一个库 -- vue-class-component
,它是 Vue 官方推出的一个支持使用 class 方式来开发 vue 单文件组件的库。
而 vue-property-decorator
正是基于此而来,它在此基础上增加了装饰器相关的功能,因此它也同时拥有 vue-class-component
的功能。
主要功能
vue-class-component 功能如下:
- methods 可以直接声明为类的成员方法
- 计算属性可以被声明为类的属性访问器
- 初始化的 data 可以被声明为类属性
- data、render 以及所有的 Vue 生命周期钩子可以直接作为类的成员方法
- 所有其他属性,需要放在装饰器中
vue-property-decorator 主要提供了多个装饰器和一个函数:
- @Prop
- @PropSync
- @Model
- @Watch
- @Provide
- @Inject
- @ProvideReactive
- @InjectReactive
- @Emit
- @Ref
- @Component (由 vue-class-component 提供)
- Mixins (由 vue-class-component 提供)
vue-class-component 主要功能
@Component
Component装饰器它注明了此类为一个Vue组件,因此即使没有设置选项也不能省略。如果需要定义比如 name、components、filters、directives以及自定义属性,就可以在Component装饰器中定义。
JavaScript 中的代码如下:
import {componentA,componentB} from '@/components';
export default{
components:{
componentA,
componentB,
},
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
}
在 TypeScript中如下:
import {Component,Vue} from 'vue-property-decorator';
import {componentA,componentB} from '@/components';
@Component({
components:{
componentA,
componentB,
},
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
})
export default class YourCompoent extends Vue{
}
Computed、Data、Methods语法
这里取消了组件的data和methods属性,以往data返回对象中的属性、methods中的方法需要直接定义在Class中,当做类的属性和方法。
@Component
export default class HelloDecorator extends Vue {
count: number = 123 // 类属性相当于以前的 data
add(): number { // 类方法就是以前的方法
this.count + 1
}
// 获取计算属性
get total(): number {
return this.count + 1
}
// 设置计算属性
set total(param:number): void {
this.count = param
}
}
vue-property-decorator 主要 API 解读
@Prop
这个装饰器是属性相关的装饰器, @Prop(options: (PropOptions | Constructor[] | Constructor) = {})
。
在使用Prop装饰器定义属性时,如果我们打开了 tsconfig.js 配置文件中的 strictpropertyinitialize 选项,我们就需要通过附加一个 !
给定义的属性,还记得这个语法吗?显式复制断言。
我们看一下 JavaScript 版的一个 Vue 代码片段:
export default{
props:{
propA:String,
propB:[String,Number],
propC:{
type:Array,
default:()=>{
return ['a','b']
},
required: true,
validator:(value) => {
return [
'a',
'b'
].indexOf(value) !== -1
}
}
}
}
如果我们使用的是 TypeScript:
import {Component,Vue,Prop} from vue-property-decorator;
@Component
export default class YourComponent extends Vue {
@Prop(String)
propA:string;
@Prop([String,Number])
propB:string|number;
@Prop({
type: String, // type: [String , Number]
default: 'default value', // 一般为String或Number
//如果是对象或数组的话。默认值从一个工厂函数中返回
// defatult: () => {
// return ['a','b']
// }
required: true,
validator: (value) => {
return [
'InProcess',
'Settled'
].indexOf(value) !== -1
}
})
propC:string;
}
@Watch
这个装饰器其实就是 Vue 中的侦听器。
@Watch(path: string, options: WatchOptions = {})
JavaScript 版 vue 中写法:
export default {
watch: {
child: [
{
handler: 'onChildChanged',
immediate: false,
deep: false
}
],
person: [
{
handler: 'onPersonChanged1',
immediate: true,
deep: true
},
{
handler: 'onPersonChanged2',
immediate: false,
deep: false
}
]
},
methods: {
onChildChanged(val, oldVal) {},
onPersonChanged1(val, oldVal) {},
onPersonChanged2(val, oldVal) {}
}
}
在 TypeScript 中:
import { Vue, Component, Watch } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Watch('child')
onChildChanged(val: string, oldVal: string) {}
@Watch('person', { immediate: true, deep: true })
onPersonChanged1(val: Person, oldVal: Person) {}
@Watch('person')
onPersonChanged2(val: Person, oldVal: Person) {}
}
使用了装饰器后明显代码简洁了不少。
@Emit
关于 Vue 中的事件的监听与触发,Vue 提供了两个函数 $emit
和 $on
。那么在 vue-property-decorator
中如何使用呢?
这就需要用到 vue-property-decorator
提供的 @Emit 装饰器。
在 JavaScript 中如下:
import Vue from 'vue';
export default {
mounted(){
this.$on('emit-todo', function(n) {
console.log(n)
})
this.emitTodo('world');
},
methods: {
emitTodo(n){
console.log('hello');
this.$emit('emit-todo', n);
}
}
}
在 TypeScript 中如下:
import {Vue, Component, Emit} from 'vue-property-decorator';
@Component({})
export default class Some extends Vue{
mounted(){
this.$on('emit-todo', function(n) {
console.log(n)
})
this.emitTodo('world');
}
@Emit()
emitTodo(n: string){
console.log('hello');
}
}
在 @Emit
装饰器的函数会在运行之后触发等同于其函数名(驼峰式会转为横杠式写法)的事件, 并将其函数传递给 $emit
. 如果我们想触发特定的事件呢,比如在 emitTodo 下触发 reset事件:
import {Vue, Component, Emit} from 'vue-property-decorator';
@Component({})
export default class "组件名" extends Vue{
@Emit('reset')
emitTodo(n: string){
}
}
我们只需要给装饰器 @Emit 传递一个事件名参数 reset,这样函数 emitTodo 运行之后就会触发 reset 事件.
在 Vue 中我们是使用 $emit
触发事件,使用 vue-property-decorator
时,可以借助 @Emit 装饰器来实现。
@Emit 修饰的函数所接受的参数会在运行之后触发事件的时候传递过去。
@Emit触发事件有两种写法:
@Emit()
不传参数,那么它触发的事件名就是它所修饰的函数名@Emit(name: string)
里面传递一个字符串,该字符串为要触发的事件名
@Model
Vue 组件提供 model: {prop?: string, event?: string}
让我们可以定制 prop 和 event。
默认情况下,一个组件上的 v-model 会把 value 用作 prop且把 input用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop来达到不同的目的,使用model选项可以回避这些情况产生的冲突。
比如:
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
// this allows using the `value` prop for a different purpose
value: String,
// use `checked` as the prop which take the place of `value`
checked: {
type: Number,
default: 0
}
},
// ...
})
在模板中使用:
<my-checkbox v-model="foo" value="some value"></my-checkbox>
此模板相当于:
<my-checkbox
:checked="foo"
@change="val => { foo = val }"
value="some value">
</my-checkbox>
使用 vue-property-decorator 提供的 @Model 改造上面的例子:
import { Vue, Component, Model} from 'vue-property-decorator';
@Component
export class myCheck extends Vue{
@Model ('change', {type: Boolean}) checked!: boolean;
}
@Model()
接收两个参数, 第一个是 event 值, 第二个是prop 的类型说明。
小结
我们通过本节学习了 TypeScript 版的 Vue Class 化的语法,要多处用到装饰器和 Class,这跟 Vue 原本的语法想去很远,但是其本质是一样的,我们可以进入实战来快速熟悉这些语法。